package com.web.text.diff;

import com.web.text.html.HtmlStrUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * 字符串对比工具，文字新增，删除做出标识
 */
public class StringComparatorUtil {
    private static final String ADD_BEGIN = "<span class='add'>";
    private static final String DEL_BEGIN = "<span class='add'>";
    private static final String ADD_END = "</span>";
    private static final String DEL_END = "</span>";

    private StringComparatorUtil() {
    }

    public static String comparator(String before, String after) {
        return comparator(before, after, true, ADD_BEGIN, ADD_END, DEL_BEGIN, DEL_END);
    }

    public static String comparator(String before, String after, boolean cleanHtmlTag) {
        return comparator(before, after, cleanHtmlTag, ADD_BEGIN, ADD_END, DEL_BEGIN, DEL_END);
    }

    public static String comparator(String before, String after, boolean cleanHtmlTag, String addBegin, String addEnd, String delBegin, String delEnd) {
        if (cleanHtmlTag) {
            before = HtmlStrUtil.cleanHtmlTag(before);
            after = HtmlStrUtil.cleanHtmlTag(after);
        }
        StringsComparator stringsComparator = new StringsComparator(before, after);
        stringsComparator.findHtmlTag();  //过滤 不对比html内容
        EditScript<Character> script = stringsComparator.getScript();
        final List<Item> list = new ArrayList<>();
        script.visit(new CommandVisitor<Character>() {
            @Override
            public void visitInsertCommand(Character character, int idx) {
                add(idx, 1, character, list);
            }

            @Override
            public void visitKeepCommand(Character character, int idx) {
                add(idx, 0, character, list);
            }

            @Override
            public void visitDeleteCommand(Character character, int idx) {
                add(idx, 2, character, list);
            }
        });
        return parseSimple(list, addBegin, addEnd, delBegin, delEnd);
    }


    private static void add(int idx, int type, Character character, List<Item> list) {
        Item item = null;
        if (list.size() != 0 && list.get(list.size() - 1).getType() == type) {
            item = list.get(list.size() - 1);
            item.getSb().append(character);
            item.setEnd(idx);
        } else {
            item = new Item(character, idx, idx, type);
            list.add(item);
        }
    }

    /**
     * 没有html变化过程
     *
     * @param list
     * @return
     */
    private static String parseSimple(List<Item> list, String addBegin, String addEnd, String delBegin, String delEnd) {
        StringBuilder sb = new StringBuilder();
        for (Item item : list) {
            if (item.getType() == 0) {
                sb.append(item.getSb());
            } else if (item.getType() == 1) {
                sb.append(addBegin).append(item.getSb()).append(addEnd);
            } else if (item.getType() == 2) {
                sb.append(delBegin).append(item.getSb()).append(delEnd);
            }
        }
        return sb.toString();
    }

    /**
     * 考虑有html变化过程
     *
     * @param list
     * @return
     */
    private static String parse(List<Item> list, String addBegin, String addEnd, String delBegin, String delEnd) {
        StringBuilder sb = new StringBuilder();
        for (Item item : list) {
            int start = sb.length();
            int end = start + item.getSb().length();
            item.setStart(start);
            item.setEnd(end);
            sb.append(item.getSb());
        }
        List<int[]> tag = HtmlStrUtil.findTag(sb.toString());
        parseArr(tag, list);
        int start = 0;
        for (Item item : list) {
            if (item.getDel() == 1 || item.getType() == 0) {
                continue;
            }
            if (item.getType() == 1) {
                sb.insert(start + item.getStart(), addBegin);
                start += addBegin.length();
                sb.insert(start + item.getEnd(), addEnd);
                start += addEnd.length();
            } else if (item.getType() == 2) {
                sb.insert(start + item.getStart(), delBegin);
                start += delBegin.length();
                sb.insert(start + item.getEnd(), delEnd);
                start += delEnd.length();
            }
        }
        return sb.toString();
    }


    /**
     * 判断字符变化位置和标签位置，找出合理位置，避免变化位置出现在标签中间
     *
     * @param tagList html标签位置
     * @param list    删除和添加字符位置
     */
    private static void parseArr(List<int[]> tagList, List<Item> list) {
        for (Item item : list) {
            if (item.getType() == 0) {
                continue;
            }
            int start = item.getStart();
            int end = item.getEnd();
            for (int[] tarArr : tagList) {
                if ((start > tarArr[0] && start < tarArr[1]) && (end > tarArr[0] && end < tarArr[1])) {
                    item.setDel(1);
                } else if ((start > tarArr[0] && start < tarArr[1]) && !(end > tarArr[0] && end < tarArr[1])) {
                    item.setStart(tarArr[1]);
                } else if (!(start > tarArr[0] && start < tarArr[1]) && (end > tarArr[0] && end < tarArr[1])) {
                    item.setEnd(tarArr[0]);
                }
            }
        }
    }

    private static class Item {
        private StringBuilder sb = new StringBuilder();
        private int start;
        private int end;
        private int type;  //0 keep  1 insert 2 delete
        private int del = 0;


        public Item(Character character, int start, int end, int type) {
            this.sb.append(character);
            this.start = start;
            this.end = end;
            this.type = type;
        }


        public StringBuilder getSb() {
            return sb;
        }

        public void setSb(StringBuilder sb) {
            this.sb = sb;
        }

        public int getStart() {
            return start;
        }

        public void setStart(int start) {
            this.start = start;
        }

        public int getEnd() {
            return end;
        }

        public void setEnd(int end) {
            this.end = end;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }

        public int getDel() {
            return del;
        }

        public void setDel(int del) {
            this.del = del;
        }
    }
}
